using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.ComponentModel;
using System.Data;
using ACME.Northwind.DataTransfer;
using ACME.Northwind.Business.Objects;
using Widgetsphere.Core.DataAccess;
using System.Linq.Expressions;
using ACME.Northwind.Business.LINQ;
using Widgetsphere.Core.Util;

namespace ACME.Northwind.DALProxy.Extensions
{
	/// <summary>
	/// This is a set of extensions for the Order DAL/DTO object
	/// </summary>
	public static partial class OrderExtension
	{
		#region Fill DAL

		/// <summary>
		/// Populate a DAL object from its corresponding DTO.
		/// </summary>
		/// <param name="item">The empty DAL object.</param>
		/// <param name="dtoItem">The source DTO to load.</param>
		public static void Fill(this Order item, OrderDTO dtoItem)
		{
			if (dtoItem == null) throw new Exception("The Order DTO cannot be null.");
			if (item == null) throw new Exception("The Order item cannot be null.");
			DataRow dr = ((DataRow)((IWrappingClass)item).WrappedClass);
			if (dtoItem.ShipVia == null) dr["ShipVia"] = System.DBNull.Value;
			else dr["ShipVia"] = dtoItem.ShipVia;
			if (dtoItem.ShipRegion == null) dr["ShipRegion"] = System.DBNull.Value;
			else dr["ShipRegion"] = dtoItem.ShipRegion;
			if (dtoItem.ShipPostalCode == null) dr["ShipPostalCode"] = System.DBNull.Value;
			else dr["ShipPostalCode"] = dtoItem.ShipPostalCode;
			if (dtoItem.ShippedDate == null) dr["ShippedDate"] = System.DBNull.Value;
			else dr["ShippedDate"] = dtoItem.ShippedDate;
			if (dtoItem.ShipName == null) dr["ShipName"] = System.DBNull.Value;
			else dr["ShipName"] = dtoItem.ShipName;
			if (dtoItem.ShipCountry == null) dr["ShipCountry"] = System.DBNull.Value;
			else dr["ShipCountry"] = dtoItem.ShipCountry;
			if (dtoItem.ShipCity == null) dr["ShipCity"] = System.DBNull.Value;
			else dr["ShipCity"] = dtoItem.ShipCity;
			if (dtoItem.ShipAddress == null) dr["ShipAddress"] = System.DBNull.Value;
			else dr["ShipAddress"] = dtoItem.ShipAddress;
			if (dtoItem.RequiredDate == null) dr["RequiredDate"] = System.DBNull.Value;
			else dr["RequiredDate"] = dtoItem.RequiredDate;
			dr["OrderID"] = dtoItem.OrderId;
			if (dtoItem.OrderDate == null) dr["OrderDate"] = System.DBNull.Value;
			else dr["OrderDate"] = dtoItem.OrderDate;
			if (dtoItem.Freight == null) dr["Freight"] = System.DBNull.Value;
			else dr["Freight"] = dtoItem.Freight;
			if (dtoItem.EmployeeId == null) dr["EmployeeID"] = System.DBNull.Value;
			else dr["EmployeeID"] = dtoItem.EmployeeId;
			if (dtoItem.CustomerId == null) dr["CustomerID"] = System.DBNull.Value;
			else dr["CustomerID"] = dtoItem.CustomerId;
		}

		#endregion

		#region Fill DTO

		/// <summary>
		/// Populate a DTO object collection from its corresponding DAL object collection.
		/// </summary>
		/// <param name="list">The destination DTO collection.</param>
		/// <param name="orderCollection">The source DAL collection.</param>
		public static void Fill(this List<OrderDTO> list, OrderCollection orderCollection)
		{
			if (orderCollection == null) throw new Exception("The Order collection cannot be null.");
			if (list == null) list = new List<OrderDTO>();
			foreach (Order item in orderCollection)
			{
				OrderDTO newItem = new OrderDTO();
				newItem.Fill(item);
				list.Add(newItem);
			}
		}

		/// <summary>
		/// Populate a DTO object from its corresponding DAL object.
		/// </summary>
		/// <param name="item">The source DAL object.</param>
		/// <param name="dtoItem">The empty DTO to load.</param>
		public static void Fill(this OrderDTO dtoItem, Order item)
		{
			if (dtoItem == null) throw new Exception("The Order DTO cannot be null.");
			if (item == null) throw new Exception("The Order item cannot be null.");
			dtoItem.ShipVia = item.ShipVia;
			dtoItem.ShipRegion = item.ShipRegion;
			dtoItem.ShipPostalCode = item.ShipPostalCode;
			dtoItem.ShippedDate = item.ShippedDate;
			dtoItem.ShipName = item.ShipName;
			dtoItem.ShipCountry = item.ShipCountry;
			dtoItem.ShipCity = item.ShipCity;
			dtoItem.ShipAddress = item.ShipAddress;
			dtoItem.RequiredDate = item.RequiredDate;
			dtoItem.OrderId = item.OrderId;
			dtoItem.OrderDate = item.OrderDate;
			dtoItem.Freight = item.Freight;
			dtoItem.EmployeeId = item.EmployeeId;
			dtoItem.CustomerId = item.CustomerId;
			dtoItem.CreatedDate = item.CreatedDate;
			dtoItem.CreatedBy = item.CreatedBy;
			dtoItem.ModifiedDate = item.ModifiedDate;
			dtoItem.ModifiedBy = item.ModifiedBy;
		}

		#endregion

		#region RunSelect

		/// <summary>
		/// Select all objects from store.
		/// </summary>
		public static void RunSelect(this List<OrderDTO> list)
		{
			if (list == null) list = new List<OrderDTO>();
			OrderCollection retval = OrderCollection.RunSelect();
			foreach (Order item in retval)
			{
				OrderDTO newItem = new OrderDTO();
				newItem.Fill(item);
				list.Add(newItem);
			}
		}

		/// <summary>
		/// Using the specified Where expression, execute a query against the database to return a result set.
		/// </summary>
		public static void RunSelect(this List<OrderDTO> list, Expression<Func<OrderQuery, bool>> where)
		{
			if (list == null) list = new List<OrderDTO>();
			list.Fill(OrderCollection.RunSelect(where));
		}

		#endregion

		#region RunSelect Paged

		/// <summary>
		/// Using the specified Where expression, execute a query against the database to return a paged result set.
		/// </summary>
		public static PagedQueryResults<OrderDTO> RunSelect(this List<OrderDTO> list, string pageSize, string page, string sortOrder, string sortColumn, string linq)
		{
			if (list == null) list = new List<OrderDTO>();
			#region Setup Variables and Error Check
			int po;
			int rpp;

			if (!int.TryParse(page, out po)) throw new Exception("The page number number is not a valid integer!");
			if (po < 1) po = 1;

			if (!int.TryParse(pageSize, out rpp)) throw new Exception("The page size is not a valid integer!");
			if (rpp < 1) rpp = 1;

			Order.FieldNameConstants sortField;
			try
			{
				sortField = (Order.FieldNameConstants)Enum.Parse(typeof(Order.FieldNameConstants), sortColumn);
			}
			catch
			{
				throw new Exception("The sort field is not valid!");
			}

			bool ascending = (sortOrder.ToLower() == "asc" || sortOrder.ToLower() == "ascending");
			#endregion

			PagedQueryResults<OrderDTO> retVal = new PagedQueryResults<OrderDTO>();
			OrderPaging paging = new OrderPaging();
			paging.RecordsperPage = rpp;
			paging.PageIndex = po;
			paging.OrderByList.Add(new OrderPagingFieldItem(sortField, ascending));

			OrderCollection dalList = null;
			if (string.IsNullOrEmpty(linq))
			{
				dalList = OrderCollection.RunSelect(x => true, paging);
			}
			else
			{
				System.Reflection.MethodInfo method = LINQDynamicCompile.GetLINQMethodPaged(linq, "ACME.Northwind.Business", "Order");
				dalList = (OrderCollection)method.Invoke(null, new object[] { paging });
			}

			if (list == null) list = new List<OrderDTO>();
			foreach (Order item in dalList)
			{
				OrderDTO newItem = new OrderDTO();
				newItem.Fill(item);
				list.Add(newItem);
			}

			retVal.CurrentPage = paging.PageIndex;
			double totalPages = Math.Ceiling((double)paging.RecordCount / (double)paging.RecordsperPage);
			retVal.TotalPages = (int)totalPages;
			retVal.TotalRecords = paging.RecordCount;
			retVal.GridData = list;

			return retVal;
		}

		#endregion

		#region RunSelect Paged

		/// <summary>
		/// Using the specified Where expression, execute a query against the database to return a result set.
		/// </summary>
		public static void RunSelect(this List<OrderDTO> list, string linq)
		{
			if (list == null) list = new List<OrderDTO>();

			OrderCollection dalList = null;
			if (string.IsNullOrEmpty(linq))
			{
				dalList = OrderCollection.RunSelect(x => true);
			}
			else
			{
				System.Reflection.MethodInfo method = LINQDynamicCompile.GetLINQMethodAll(linq, "ACME.Northwind.Business", "Order");
				dalList = (OrderCollection)method.Invoke(null, new object[] { });
			}

			if (list == null) list = new List<OrderDTO>();
			foreach (Order item in dalList)
			{
				OrderDTO newItem = new OrderDTO();
				newItem.Fill(item);
				list.Add(newItem);
			}
		}

		#endregion

		#region Persist

		/// <summary>
		/// Persists this object to store.
		/// </summary>
		public static OrderDTO Persist(this OrderDTO order)
		{
			if (order == null) throw new Exception("The Order DTO cannot be null.");
			//Find this object and if not exists, create a new one
			Order newItem = null;
			OrderCollection newCollection = null;

			bool isIdentityChecked = false;
			isIdentityChecked = true;
			if(isIdentityChecked)
			{
				if (order.OrderId > 0)
				{
					newItem = ACME.Northwind.Business.Objects.Order.SelectUsingPK(order.OrderId);
					newCollection = newItem.ParentCollection;
				}
				else
				{
					if (order.OrderId == 0) order.OrderId = -1;
					newCollection = new OrderCollection();
					newItem = newCollection.NewItem();
				}
			}
			else
			{
				newItem = ACME.Northwind.Business.Objects.Order.SelectUsingPK(order.OrderId);
				if (newItem == null)
				{
					newCollection = new OrderCollection();
					newItem = newCollection.NewItem();
				}
				else
				{
					newCollection = newItem.ParentCollection;
				}
			}

			//Populate the DAL and perist
			newItem.Fill(order);
			if (!newItem.IsParented) newCollection.AddItem(newItem);
			newCollection.Persist();

			//Re-populate the passed-in object from the DAL
			order.Fill(newItem);
			return order;
		}

		/// <summary>
		/// Persists this collection to store.
		/// </summary>
		public static bool Persist(this List<OrderDTO> orderList)
		{
			if (orderList == null) throw new Exception("The Order DTO list cannot be null.");
			//Get all objects from the database that match PK
			OrderCollection orderCollection = new OrderCollection();
			List<OrderPrimaryKey> keyList = new List<OrderPrimaryKey>();
			foreach (OrderDTO dto in orderList)
			{
				keyList.Add(new OrderPrimaryKey(dto.OrderId));
			}			
			orderCollection.SubDomain.AddSelectCommand(new ACME.Northwind.Business.SelectCommands.OrderSelectByPks(keyList));
			orderCollection.SubDomain.RunSelectCommands();

			//Now find and populate the objects for saving
			foreach (OrderDTO dto in orderList)
			{
				Order order = (from x in (IEnumerable<Order>)orderCollection
					where x.OrderId == dto.OrderId
					select x).FirstOrDefault();

				if (order == null) order = orderCollection.NewItem();
				order.Fill(dto);
				if (!order.IsParented) orderCollection.AddItem(order);
			}
			orderCollection.Persist();

			//Re-populate the passed-in object from the DAL
			List<OrderDTO> deletedList = new List<OrderDTO>();
			foreach (OrderDTO dto in orderList)
			{
				Order order = (from x in (IEnumerable<Order>)orderCollection
					where x.OrderId == dto.OrderId
					select x).FirstOrDefault();

				if (order != null) dto.Fill(order);
				else deletedList.Add(dto);
			}

			//Remove the items that are no longer there (if any)
			foreach (OrderDTO dto in deletedList)
			{
				orderList.Remove(dto);
			}

			return true;
		}

		#endregion

		#region Order Select by Column

		#endregion

		#region Order SelectUsingPK

		/// <summary>
		/// Select a single object from this collection by its primary key.
		/// </summary>
		public static bool SelectUsingPK(this OrderDTO order, int orderid)
		{
			Order item = ACME.Northwind.Business.Objects.Order.SelectUsingPK(orderid);
			if (item == null) return false;
			order.Fill(item);
			return true;
		}

		#endregion

		#region Order Select By Foreign Key

		/// <summary>
		/// Populate a 'Customer' DTO object collection from its relationship with the 'Order' table.
		/// </summary>
		public static void SelectByCustomer(this List<OrderDTO> OrderList, string customerid)
		{
			OrderCollection list = OrderCollection.RunSelect(x => x.CustomerId == customerid);
			foreach (Order item in list)
			{
				OrderDTO newItem = new OrderDTO();
				newItem.Fill(item);
				OrderList.Add(newItem);
			}
		}

		/// <summary>
		/// Populate a 'Employee' DTO object collection from its relationship with the 'Order' table.
		/// </summary>
		public static void SelectByEmployee(this List<OrderDTO> OrderList, int employeeid)
		{
			OrderCollection list = OrderCollection.RunSelect(x => x.EmployeeId == employeeid);
			foreach (Order item in list)
			{
				OrderDTO newItem = new OrderDTO();
				newItem.Fill(item);
				OrderList.Add(newItem);
			}
		}

		/// <summary>
		/// Populate a 'Shipper' DTO object collection from its relationship with the 'Order' table.
		/// </summary>
		public static void SelectByShipper(this List<OrderDTO> OrderList, int shipperid)
		{
			OrderCollection list = OrderCollection.RunSelect(x => x.ShipVia == shipperid);
			foreach (Order item in list)
			{
				OrderDTO newItem = new OrderDTO();
				newItem.Fill(item);
				OrderList.Add(newItem);
			}
		}

		#endregion

		#region Order Delete

		/// <summary>
		/// Delete the specified item
		/// </summary>
		public static bool Delete(this OrderDTO order)
		{
			Order item = ACME.Northwind.Business.Objects.Order.SelectUsingPK(order.OrderId);
			if (item == null) return false;
			item.Delete();
			item.Persist();
			return true;
		}

		#endregion
	}

}
